package com.menghao.rpc.consumer.handle.http;

import com.menghao.rpc.consumer.balance.LoadBalancer;
import com.menghao.rpc.consumer.balance.RandomLoadBalancer;
import com.menghao.rpc.consumer.handle.ReferenceAgent;
import com.menghao.rpc.consumer.model.ReferenceKey;
import com.menghao.rpc.consumer.model.RpcRequest;
import com.menghao.rpc.exception.InvokeException;
import com.menghao.rpc.provider.model.RpcResponse;
import com.menghao.rpc.spring.BeansManager;
import lombok.Getter;
import lombok.Setter;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.List;

/**
 * <p>ReferenceAgent Http方式实现.</br>
 * <p>调用原始接口的任意方法会被该类的invoke方法代理：使用RestTemplate发送请求</p>
 * <p>sourceInterface/implCode：唯一标识一个服务</p>
 *
 * @author MarvelCode
 */
public class HttpReferenceAgent implements ReferenceAgent {

    @Getter
    private Class sourceInterface;
    @Getter
    private String implCode;
    @Setter
    private List<String> providerHosts;
    private RestTemplate restTemplate;

    private LoadBalancer defaultBalancer = new RandomLoadBalancer();

    public HttpReferenceAgent(ReferenceKey referenceKey) {
        this.sourceInterface = referenceKey.getSourceInterface();
        this.implCode = referenceKey.getName();
        this.restTemplate = BeansManager.getInstance().getBeanByType(RestTemplate.class);
    }

    @Override
    public Object invoke(Method method, Object[] args) {
        String url = select();
        // 构造请求参数
        RpcRequest apiParam = makeParam(method, args);
        // 发送Http请求
        ResponseEntity<RpcResponse> responseEntity = restTemplate.postForEntity(url, apiParam, RpcResponse.class);
        if (responseEntity.getStatusCode() == HttpStatus.OK) {
            if (responseEntity.getBody().getResult() != null) {
                return responseEntity.getBody().getResult();
            }
            if (responseEntity.getBody().getThrowable() != null) {
                throw new InvokeException(responseEntity.getBody().getThrowable());
            }
        }
        return new InvokeException(MessageFormat.format("invoke {0} response status code {1}",
                sourceInterface.getName() + ":" + method.getName(), responseEntity.getStatusCode()));
    }


    private String select() {
        if (providerHosts == null || providerHosts.size() == 0) {
            throw new InvokeException("There are currently no service providers available");
        }
        // 负载均衡
        String ip = defaultBalancer.select(providerHosts);
        return "http://" + ip + "/marvel/rpc/entrance";
    }
}
